#include "bib_Socket.h"

int abrirSocket(int *miSock){

	if ((*miSock = socket(PF_INET, SOCK_STREAM, 0)) < 0){
		imprimirLog(ERROR, "Error al crear el socket:", strerror(errno));
		*miSock = 0;
		return FIN_ERROR;
	}
	
	imprimirLogNumerico(DEBUG, "socket OK:", *miSock);
	return FIN_OK;
}
/*=====================================================================================*/
int cerrarSocket(int *miSock){

	if (*miSock){
		if (close(*miSock) < 0){
			imprimirLog(ERROR, "close:", strerror(errno));
			return FIN_ERROR;
		}

		imprimirLogNumerico(DEBUG, "close OK:", *miSock);
		*miSock = 0;
	}
	
	return FIN_OK;
}
/*=====================================================================================*/
int enlazarPuerto(int *miSock, int miPuerto){

	struct sockaddr_in miAddr;
	int yes = 1;

	/*para que no me diga si lo tengo que volver a correr al programa que el puerto esta ocupado*/
	if (setsockopt(*miSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
		imprimirLog(WARNING, "setsockopt:", strerror(errno));
		
	miAddr.sin_family = AF_INET;
	miAddr.sin_addr.s_addr = INADDR_ANY;
	miAddr.sin_port = htons(miPuerto);
	memset(miAddr.sin_zero, '\0', 8);
	
	if (bind(*miSock, (struct sockaddr *) &miAddr, sizeof (miAddr)) < 0){
		imprimirLog(ERROR, "bind:", strerror(errno));
		cerrarSocket(miSock);
		return FIN_ERROR;
	}
	
	imprimirLogNumerico(DEBUG, "bind OK:", *miSock);
	return FIN_OK;
}
/*=====================================================================================*/
int escuchar(int *miSock){

	if (listen(*miSock, TAM_COLA_ESPERA) < 0){
		imprimirLog(ERROR, "listen:", strerror(errno));
		cerrarSocket(miSock);
		return FIN_ERROR;
	}
	
	imprimirLogNumerico(DEBUG, "listen OK:", *miSock);
	return FIN_OK;
}
/*=====================================================================================*/
int aceptarConexion(int *miSock, int *sockEntrante, struct sockaddr *addrEntrante){

	socklen_t len = sizeof(struct sockaddr);
	
	if ((*sockEntrante = accept(*miSock, addrEntrante, &len)) < 0){
		imprimirLog(ERROR, "accept:", strerror(errno));
		cerrarSocket(miSock);
		*sockEntrante = 0;
		return FIN_ERROR;
	}
	
	imprimirLogNumerico(DEBUG, "accept OK:", *sockEntrante);
	return FIN_OK;
}
/*=====================================================================================*/
int conectar(int *miSock, int puertoServidor, char *IPServidor){
	struct sockaddr_in addrDestino;
	
	addrDestino.sin_family = AF_INET;
	addrDestino.sin_port = htons(puertoServidor);
	addrDestino.sin_addr.s_addr = inet_addr(IPServidor);
	memset((addrDestino.sin_zero), '\0', 8);
	
	if (connect(*miSock, (struct sockaddr *) &addrDestino, sizeof(struct sockaddr)) == -1){
		imprimirLog(ERROR, "connect:", strerror(errno));
		cerrarSocket(miSock);
		return FIN_ERROR;
	}
	
	imprimirLogNumerico(DEBUG, "connect OK:", *miSock);
	return FIN_OK;
}
/*=====================================================================================*/
int sendall(int sockEntrante, char *buf, int *len){
	int bytesRestantes = *len;
	int n;
	int total = 0;	/*cantidad bytes enviados*/
	
	while (total < *len){
		n = send(sockEntrante, buf + total, bytesRestantes, 0);
		if (n == -1){
			imprimirLog(ERROR, "send:", strerror(errno));
			break;
		}
		total += n;
		bytesRestantes -= n;
	}
	
	*len = total;
	return n == -1 ? FIN_ERROR : FIN_OK;
}
/*=====================================================================================*/
int reciveall(int sock, char *buf, int *len){

	int bytesRestantes = *len;
	int n;
	int total = 0;	/*cantidad bytes recibidos*/
	
	while (total < *len){
		n = recv(sock, buf + total, bytesRestantes, 0);
		
		if (n == -1){
			imprimirLog(ERROR, "recv:", strerror(errno));
			break;
		}
		
		if (!n){
			imprimirLog(ERROR, "recv:", "La maquina remota finalizo la conexion");
			break;
		}
		
		total += n;
		bytesRestantes -= n;
	}
	
	*len = total;
	
	if (n == -1)
		return FIN_ERROR;
		
	else if (!n)
		return FIN_WARNING;
		
	return FIN_OK;
}
/*=====================================================================================*/
int verificarDescriptores(int maximo, fd_set *socketsLectura){

	if (select(maximo + 1, socketsLectura, NULL, NULL, NULL) < 0){
		imprimirLog(ERROR, "select:", strerror(errno));
		return FIN_ERROR;
	}

	return FIN_OK;
}
/*=====================================================================================*/
int abrirSocketServidor(int *miSock, int miPuerto){
	
	if (abrirSocket(miSock))
		return FIN_ERROR;
		
	if (enlazarPuerto(miSock, miPuerto))
		return FIN_ERROR;
	
	if (escuchar(miSock))
		return FIN_ERROR;
	
	imprimirLogNumerico(INFO, "Socket creado correctamente", *miSock);
	return FIN_OK;
}
/*=====================================================================================*/
int abrirSocketCliente(int *miSock, int puertoServidor, char *IPServidor){
	
	if (abrirSocket(miSock))
		return FIN_ERROR;
		
	if (conectar(miSock, puertoServidor, IPServidor))
		return FIN_ERROR;
		
	imprimirLogNumerico(INFO, "Socket creado correctamente", *miSock);
	return FIN_OK;
}
/*=====================================================================================*/
int enviarMensajeNIPC(int sock, struct HEADER_NIPC *header, char *payload){
	
	char *mensaje;
	int len = sizeof(HEADER_NIPC) + header->payloadLength;
	
	if (!(mensaje = (char *) malloc(len))){
		imprimirLog(ERROR, "malloc:", strerror(errno));
		return FIN_ERROR;
	}
	
	memcpy(mensaje, header, sizeof(HEADER_NIPC));
	if (header->payloadLength)
		memcpy(mensaje + sizeof(HEADER_NIPC), payload, (size_t) header->payloadLength);
		
	if (sendall(sock, mensaje, &len)){
		free(mensaje);
		return FIN_ERROR;
	}

	free(mensaje);
	return FIN_OK;
}
/*=====================================================================================*/
int recibirMensajeNIPC(int sock, struct HEADER_NIPC *header, char *payload){

	int respuesta;
	int len = sizeof(HEADER_NIPC);
	
	if ((respuesta = reciveall(sock, (char *) header, &len)))
		return respuesta;

	len = header->payloadLength;

	if (header->payloadLength > 0)
		if ((respuesta = reciveall(sock, payload, &len)))
			return respuesta;

	return FIN_OK;
}
/*=====================================================================================*/
int recibirMensajeNIPCDinamico(int sock, struct HEADER_NIPC *header, char **payload){
	
	int len = sizeof(HEADER_NIPC);
	char respuesta;
	
	if ((respuesta = reciveall(sock, (char *) header, &len)))
		return respuesta;

	len = header->payloadLength;
	*payload = NULL;

	if (header->payloadLength > 0){
		if (!(*payload = (char *) malloc(sizeof(char) * len))){
			imprimirLog(ERROR, "malloc:", strerror(errno));
			return FIN_ERROR;
		}
			
		if ((respuesta = reciveall(sock, *payload, &len))){
			free(*payload);
			*payload = NULL;
			return respuesta;
		}
	}
	
	return FIN_OK;
}
/*=====================================================================================*/
int abrirSocketUnix(int *miSock){

	struct sockaddr_un miAddr;
	int yes = 1;

	if ((*miSock = socket (AF_UNIX, SOCK_STREAM, 0)) < 0){
		imprimirLog(ERROR, "Error al crear el socket Unix:", strerror(errno));
		*miSock = 0;
		return FIN_ERROR;
	}
	imprimirLogNumerico(DEBUG, "socket (AF_UNIX) OK:", *miSock);
	
	////Esto no aparece en el ejemplo
	if (setsockopt(*miSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
		imprimirLog(WARNING, "setsockopt:", strerror(errno));
	
	memset(&miAddr, 0, sizeof(miAddr));
	miAddr.sun_family = AF_UNIX;
	strcpy(miAddr.sun_path, SERVER_PATH);
	
	if (bind(*miSock, (struct sockaddr *) &miAddr, SUN_LEN(&miAddr)) < 0){
		imprimirLog(ERROR, "bind (AF_UNIX):", strerror(errno));
		cerrarSocket(miSock);
		return FIN_ERROR;
	}
	imprimirLogNumerico(DEBUG, "bind (AF_UNIX) OK:", *miSock);

	//Listen
	if (escuchar(miSock)){
		imprimirLog(ERROR, "listen (AF_UNIX):", strerror(errno));
		cerrarSocket(miSock);
		return FIN_ERROR;
	}
	
	imprimirLogNumerico(INFO, "Socket (AF_UNIX) creado correctamente", *miSock);
	
	return FIN_OK;
}
/*=====================================================================================*/
int conectarSocketUnix(int *miSock){

	struct sockaddr_un addrDestino;

	if ((*miSock = socket (AF_UNIX, SOCK_STREAM, 0)) < 0){
		imprimirLog(ERROR, "Error al crear el socket Unix:", strerror(errno));
		*miSock = 0;
		return FIN_ERROR;
	}
	imprimirLogNumerico(DEBUG, "socket (AF_UNIX) OK:", *miSock);

	memset(&addrDestino, 0, sizeof(addrDestino));
	addrDestino.sun_family = AF_UNIX;
	strcpy(addrDestino.sun_path, SERVER_PATH);

	if (connect(*miSock, (struct sockaddr *)&addrDestino,SUN_LEN(&addrDestino)) == -1){
		imprimirLog(ERROR, "connect:", strerror(errno));
		cerrarSocket(miSock);
		return FIN_ERROR;
	}

	imprimirLogNumerico(DEBUG, "connect (AF_UNIX) OK:", *miSock);

	return FIN_OK;
}
